/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.cratos.route.gateway;

import java.lang.reflect.Type;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.Resource;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.http.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;

/**
 *
 * @author admin
 */
@RestWebSocket(name = "wsim", catalog = "ws", wsmaxconns = 10000, comment = "WebSocket服务", repair = false)
public class GateWayWebSocket extends WebSocket<Integer, Object> {

    protected static final byte[] LINE = new byte[]{'\r', '\n'};

    @Comment("sessionid 平台")
    private String jsessionId = "";

    @Comment("unf 平台")
    private String unf = "";

    public static class SimpleUser {

        public int userid;

        public String username = "";

        public String jsessionid = "";

    }

    private String checkUser = "";
    private static final Type RET_USER_TYPE = new TypeToken<RetResult<SimpleUser>>() {
    }.getType();

    private static final Type RET_RETSTRING_TYPE = new TypeToken<RetResult<String>>() {
    }.getType();

    protected HttpContext context;

    protected int userid;

    protected Map<String, String> userHeader;

    @Resource
    protected GateWayService service;

    @Resource
    protected JsonConvert convert = JsonConvert.root();

    protected String currmodule = "platf"; //当前用户正在玩的游戏

    //key为模块名
    protected Map<String, ModuleNodeAddress> myModuleNodes = new ConcurrentHashMap<>();

    @Override
    public CompletableFuture<Boolean> onSingleRepeatConnect() {
        return sendMessage(MessageResp.buildRepeatedLogin(new MessageResp.RepeatedLoginResult("用户在其他终端已登录!")), getUserid()).thenCompose(c -> {
            getLogger().finest("onSingleRepeatConnect === send status ===" + c);
            return forceCloseWebSocket(getUserid()).thenApply((r) -> true);
        });
    }

    @Override
    protected CompletableFuture<String> onOpen(final HttpRequest request) {
        this.context = request.getContext();
        final boolean finest = getLogger().isLoggable(Level.FINEST);
        if (finest) {
            getLogger().finest("WebSocket.req = " + request);
        }
        try {
            final ModuleNodeAddress mnode = this.service.loadHttpAddress("platf", "0", myModuleNodes);
            InetSocketAddress addr = mnode.address;
            long s = System.currentTimeMillis();
            this.jsessionId = request.getSessionid(false);
            if (this.jsessionId == null || this.jsessionId.isEmpty()) {
                jsessionId = request.getParameter("JSESSIONID", request.getParameter("jsessionid", request.getParameter("jsessionId")));
            }
//            if (jsessionId == null || jsessionId.isEmpty()) jsessionId = request.getHeader("JSESSIONID");
            this.userHeader = Utility.ofMap(
                    "App-Agent", request.getParameter("appagent", request.getHeader("App-Agent", request.getHeader("User-Agent", "None"))),
                    "X-RemoteHost", request.getHost(),
                    "X-RemoteAddress", request.getRemoteAddr(),
                    "JSESSIONID", this.jsessionId,
                    "LOCAL", request.getHeader("LOCAL", "zh_CN"),
                    "WS-SncpAddress", JsonConvert.root().convertTo(getSncpAddress())
            );
            String unfcookie = request.getCookie("UNF");
            if (unfcookie == null || unfcookie.isEmpty()) {
                unfcookie = request.getParameter("UNF", request.getParameter("unf"));
            }
            if (unfcookie == null || unfcookie.isEmpty()) {
                unfcookie = request.getHeader("UNF");
            }
            if (unfcookie != null && !unfcookie.isEmpty()) {
                this.userHeader.put("UNF", unfcookie);
            }
            this.userHeader.put("User-Agent", this.userHeader.get("App-Agent"));
            if (this.jsessionId == null && unfcookie == null) {
                return send("{\"onLoginFailMessage\":{\"retcode\":30010001,\"retinfo\":\"系统通讯错误\"}}").thenApply(x -> null);
            }
            mnode.semaphore.acquire();
            try {
                checkUser = checkUser();
                if (finest) {
                    getLogger().finest("login.http.url.checkUser = " + checkUser);
                }
            } finally {
                mnode.semaphore.release();
            }
            RetResult<SimpleUser> ret = JsonConvert.root().convertFrom(RET_USER_TYPE, checkUser);
            if (!ret.isSuccess()) {
                return send("{\"onLoginFailMessage\":{\"retcode\":30010001,\"retinfo\":\"系统通讯错误\"}}").thenApply(x -> null);
            }
            this.jsessionId = ret.getResult().jsessionid;// 更新jsessionid
            this.userid = ret.getResult().userid;
            this.userHeader.put("Route-Userid", "" + this.userid);
            this.userHeader.put("JSESSIONID", this.jsessionId);
            if (finest) {
                getLogger().finest("login.http.url.result = " + checkUser);
            }
            long e = System.currentTimeMillis() - s;
            if (e > 6000) {
                getLogger().finer("获取登陆数据耗时过长: " + request.getParameter("JSESSIONID"));
            }
        } catch (Exception e) {
            return send("{\"onLoginFailMessage\":{\"retcode\":30010001,\"retinfo\":\"系统通讯错误\"}}").thenApply(x -> null);
        }
        return CompletableFuture.completedFuture(jsessionId);
    }

    @Override
    protected CompletableFuture<Integer> createUserid() {
        return CompletableFuture.completedFuture(userid);
    }

    @Override
    @Comment("连接后事件响应")
    public CompletableFuture onConnected() {
        send("{\"onUserLoginMessage\":" + checkUser + "}");
        // 添加在线用户// 
        this.enter();
        return null;
    }

    @Comment("进入系统")
    private void enter() {
        Logger logger = getLogger();
        ModuleNodeAddress mnode = this.service.loadHttpAddress(this.currmodule, "0", myModuleNodes);
        InetSocketAddress gameaddr = mnode.address;
        if (gameaddr != null) {
            String url = "http://" + gameaddr.getHostString() + ":" + gameaddr.getPort() + "/wpipes/message" + "/enter";
            try {
                mnode.semaphore.acquire();
                String result = Utility.getHttpContent(url, 10000, userHeader, null);
                logger.finest("onClose--" + url + ", result=" + result);
            } catch (Exception e) {
                logger.log(Level.WARNING, url + " send error", e);
            } finally {
                mnode.semaphore.release();
            }
        }
    }

    @Override
    @Comment("断线后事件响应")
    public CompletableFuture onClose(int code, String reason) {
        Logger logger = getLogger();
        final boolean finest = logger.isLoggable(Level.FINEST);
        if (finest) {
            logger.finest("WebSocket.onClose: userid = " + userid + ", code = " + code + ", reason = " + reason);
        }
        ModuleNodeAddress mnode = this.service.loadHttpAddress(this.currmodule, "0", myModuleNodes);
        InetSocketAddress gameaddr = mnode.address;
        if (gameaddr != null) {
            String url = "http://" + gameaddr.getHostString() + ":" + gameaddr.getPort() + "/wpipes/message" + "/leave";
            try {
                mnode.semaphore.acquire();
                String result = Utility.getHttpContent(url, 10000, userHeader, null);
                logger.finest("onClose--" + url + ", result=" + result);
            } catch (Exception e) {
                logger.log(Level.WARNING, url + " send error", e);
            } finally {
                mnode.semaphore.release();
            }
        }
        return null;
    }

    @Comment("刷新用户登陆态")
    String checkUser() {
        ModuleNodeAddress mnode = this.service.loadHttpAddress("platf", "0", myModuleNodes);
        InetSocketAddress addr = mnode.address;
        String url = "http://" + addr.getHostString() + ":" + addr.getPort() + "/wpipes/user/check/";
        try {
            mnode.semaphore.acquire();
            return Utility.getHttpContent(url, 3000, this.userHeader, null);
        } catch (Exception e) {
            getLogger().log(Level.WARNING, url + " send error", e);
        } finally {
            mnode.semaphore.release();
        }
        return null;
    }

    public static void main(String[] args) {
        try {
            Utility.getHttpContent("http://localhost:5001/wpipes/user/check/", 3000, null, null);
        } catch (Exception e) {
        } finally {
        }
    }

    @Override
    public void onPing(byte[] bytes) {
        sendPong(bytes); //To change body of generated methods, choose Tools | Templates.
    }
    
    @RestOnMessage(name = "message", comment = "接收WS通用消息")
    public void message(
            @Comment("前缀") String pipes,
            @Comment("模块名称") String module,
            @Comment("请求参数") String action,
            @Comment("HTTP请求唯一序号") String http_seqno,
            @Comment("HTTP请求头") Map<String, String> http_headers,
            @Comment("节点ID，只取最后两位") String nodeid,
            @Comment("验证码KEY") String captchakey,
            @Comment("验证码CODE") String captchacode,
            @Comment("请求参数, 格式: bean1={}&=name=xxx") String params) {
        InetSocketAddress addr = null;
        try {
            if (getLogger().isLoggable(Level.FINEST)) {
                getLogger().finest("module = " + module + ", action = " + action + ", userid = " + userid + ", nodeid = " + nodeid + ", params = " + URLDecoder.decode(params, "UTF-8"));
            }

            final ModuleNodeAddress mnode = this.service.loadHttpAddress(module, nodeid, myModuleNodes);
            addr = mnode.address;
            if (pipes == null || pipes.isEmpty()) {
                pipes = "wpipes";
            }
            final int sub = module.lastIndexOf('_');
            String url = "http://" + addr.getHostString() + ":" + addr.getPort()
                    + "/" + pipes + "/" + (sub > 0 ? module.substring(sub + 1) : module) + "/" + action + "?userid=" + getUserid();
            if (params != null && !params.isEmpty()) {
                url += "&" + params;
            }

            if (http_headers == null) {
                http_headers = userHeader;
            } else {
                http_headers.putAll(userHeader);
            }
            mnode.semaphore.acquire();
            try {
                synchronized (this) {
                    String content = "";
                    try {
                        content = Utility.getHttpContent(url, 6000, http_headers, null);
                        char ch = content.charAt(0);
                        boolean json = ch == '{' || ch == '[';
                        /**
                         * 消息发送相关业务逻辑
                         */
                        if ("onMessage".equals(action) && "message".equals(module)) {
                            send(Utility.ofMap("onHttpResponseMessage", Utility.ofMap("http_seqno", http_seqno, "content", content)));
                            return;
                        }
                        send("{\"onHttpResponseMessage\":{\"content\":" + (json ? "" : "\"") + content + (json ? "" : "\"") + ", \"http_seqno\":\"" + http_seqno + "\"" + ", \"success\":\"" + true + "\"" + "}}");
                    } catch (Exception e) {
                        getLogger().log(Level.WARNING, url + " send error", e);
                        send(MessageResp.buildReqTimeOut(new MessageResp.ReqTimeOutResult("请求超时！")));
                    }
                }
            } finally {
                mnode.semaphore.release();
            }
            //}
        } catch (Exception ex) {
            getLogger().log(Level.WARNING, "module:" + module + ", action:" + action + ", nodeid:" + nodeid + ", addr:" + addr + ", params:" + params + " send error", ex);
        }
    }

}
